﻿using System;
using System.Collections.Generic;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
using System.Windows.Forms;
using System.Reflection;

namespace AutomationFramework
{
    /// <summary>
    /// Provider for the MeasurementControl.
    /// </summary>
    public class ControlProvider : FragmentRootProvider, IValueProvider, IExpandCollapseProvider, IToggleProvider, IInvokeProvider
    {
        #region Constants
        private const int ourProviderOptionUseComThreading = 0x20;
        private const int ourProviderDescriptionId = 30107; //from MSDN 
        #endregion

        #region Fields
        private Control myControl;
        private ApiMapping myApiMapping;
        #endregion

        #region Constructors
        public ControlProvider(Control control, string mappingFile)
        {
            this.myControl = control;
            myApiMapping = new ApiMapping(mappingFile);

            // Populate static properties            
            // Next Line Commented since we have overridden NameProperty.
            //AddAutomationProperty(AutomationElementIdentifiers.NameProperty.Id, this.myControl.Name);

            AddAutomationProperty(AutomationElementIdentifiers.ClassNameProperty.Id, myApiMapping.ControlClassName);
            AddAutomationProperty(AutomationElementIdentifiers.ControlTypeProperty.Id, ControlType.Custom.Id);
            AddAutomationProperty(AutomationElementIdentifiers.LocalizedControlTypeProperty.Id, myApiMapping.ControlLocalizedControlTypeProperty);
            AddAutomationProperty(ourProviderDescriptionId, myApiMapping.ControlProviderDescriptionId);
            AddAutomationProperty(AutomationElementIdentifiers.HelpTextProperty.Id, myApiMapping.ControlHelpTextProperty);

            // The WinForm name for this control makes a good Automation ID.
            AddAutomationProperty(AutomationElementIdentifiers.AutomationIdProperty.Id, this.myControl.Name);
            AddAutomationProperty(AutomationElementIdentifiers.IsKeyboardFocusableProperty.Id, true);
            AddAutomationProperty(AutomationElementIdentifiers.IsControlElementProperty.Id, true);
            AddAutomationProperty(AutomationElementIdentifiers.IsContentElementProperty.Id, true);
            AddAutomationProperty(AutomationElementIdentifiers.IsEnabledProperty.Id, true);

            // Set Implemented Patterns.
            if (myApiMapping.ControlIValueProviderAvailable)
            {
                AddAutomationProperty(AutomationElementIdentifiers.IsValuePatternAvailableProperty.Id, true);
            }
            if (myApiMapping.ControlIExpandCollapseProviderAvailable)
            {
                AddAutomationProperty(AutomationElementIdentifiers.IsExpandCollapsePatternAvailableProperty.Id, true);
            }
            if (myApiMapping.ControlIToggleProviderAvailable)
            {
                AddAutomationProperty(AutomationElementIdentifiers.IsTogglePatternAvailableProperty.Id, true);
            }
            if (myApiMapping.ControlIInvokeProviderAvailable)
            {
                AddAutomationProperty(AutomationElementIdentifiers.IsInvokePatternAvailableProperty.Id, true);
            }

            // Some properties are provided for me already by HWND provider like
            // NativeWindowHandle, ProcessId, FrameworkId, IsEnabled, HasKeyboardFocus
        }
        #endregion

        #region Overridden Methods (Override base default behavior)

        public override ProviderOptions ProviderOptions
        {
            get
            {
                return (ProviderOptions)((int)ProviderOptions.ServerSideProvider | ourProviderOptionUseComThreading);
            }
        }

        public override object GetPatternProvider(int patternId)
        {
            //return the patterns we support.
            if (myApiMapping.ControlIValueProviderAvailable && patternId == ValuePatternIdentifiers.Pattern.Id)
            {
                return this;
            }
            if (myApiMapping.ControlIExpandCollapseProviderAvailable && patternId == ExpandCollapsePatternIdentifiers.Pattern.Id)
            {
                return this;
            }
            if (myApiMapping.ControlIToggleProviderAvailable && patternId == TogglePatternIdentifiers.Pattern.Id)
            {
                return this;
            }
            if (myApiMapping.ControlIInvokeProviderAvailable && patternId == InvokePatternIdentifiers.Pattern.Id)
            {
                return this;
            }

            return base.GetPatternProvider(patternId);
        }

        protected override IntPtr GetWindowHandle()
        {
            // Return our window handle, since we're a root provider
            return this.myControl.Handle;
        }

        protected override string GetName()
        {
            return this.myControl.Name;
        }

        protected override IRawElementProviderFragment GetFirstChild()
        {
            //object obj = myObject.GetType().GetProperty(ApiMapping.ObjectIsLeaf).GetValue(myObject, null);
            //bool isLeaf = Convert.ToBoolean(obj);
            //if (!isLeaf)
            {
                Object propertyValue = myControl.GetType().GetProperty(myApiMapping.ControlItems).GetValue(myControl, null);
                if (propertyValue != null && propertyValue.GetType().IsArray)
                {
                    Array array = propertyValue as Array;

                    Object item = array.GetValue(0);
                    if (item != null)
                    {
                        // Return our first child, which is the fragment for Header
                        return new ObjectProvider(this.myControl, this, item, myApiMapping);
                    }
                }
            }
            return null;
        }

        protected override IRawElementProviderFragment GetLastChild()
        {
            Object propertyValue = myControl.GetType().GetProperty(myApiMapping.ControlItems).GetValue(myControl, null);
            if (propertyValue != null && propertyValue.GetType().IsArray)
            {
                Array array = propertyValue as Array;

                Object item = array.GetValue(array.Length - 1);
                if (item != null)
                {
                    // Return our first child, which is the fragment for Header
                    return new ObjectProvider(this.myControl, this, item, myApiMapping);
                }
            }
            return null;
        }

        /// <summary>
        /// Check to see if the passed point is a hit on one of our children
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public override IRawElementProviderFragment ElementProviderFromPoint(double x, double y)
        {
            if (myApiMapping.ControlElementProviderFromPointAvailable)
            {
                //Convert screen point to client point
                System.Drawing.Point clientPoint = this.myControl.PointToClient(
                    new System.Drawing.Point((int)x, (int)y));

                // do a hit test and see what item this is

                object[] parameters = new object[] { clientPoint };
                object value = myControl.GetType().GetMethod(myApiMapping.ControlElementProviderFromPoint).Invoke(myControl, parameters);

                if (value != null)
                {
                    // Return the appropriate fragment
                    return new ObjectProvider(this.myControl, this, value, myApiMapping);
                }
            }
            return null;
        }

        #endregion

        #region IValueProvider Members

        public bool IsReadOnly
        {
            get { return myApiMapping.ControlIValueProviderIsReadOnly; }
        }

        public void SetValue(string value)
        {
            if (myApiMapping.ControlIValueProviderAvailable)
            {
                myControl.GetType().GetProperty(myApiMapping.ControlIValueProviderValue).SetValue(myControl, value, null);
            }
        }

        public string Value
        {
            get
            {
                string name = null; ;
                if (myApiMapping.ControlIValueProviderAvailable)
                {
                    name = myControl.GetType().GetProperty(myApiMapping.ControlIValueProviderValue).GetValue(myControl, null).ToString();
                }
                return name;
            }
        }

        #endregion

        #region IExpandCollapseProvider Members

        public void Collapse()
        {
            if (myApiMapping.ControlIExpandCollapseProviderAvailable)
            {
                myControl.GetType().GetProperty(myApiMapping.ControlIExpandCollapseCollapse).SetValue(myControl, false, null);
            }
        }

        public void Expand()
        {
            if (myApiMapping.ControlIExpandCollapseProviderAvailable)
            {
                myControl.GetType().GetProperty(myApiMapping.ControlIExpandCollapseCollapse).SetValue(myControl, true, null);
            }
        }

        public ExpandCollapseState ExpandCollapseState
        {
            get
            {
                if (myApiMapping.ControlIExpandCollapseProviderAvailable)
                {
                    bool state = Convert.ToBoolean(myControl.GetType().GetProperty(myApiMapping.ControlIExpandCollapseCollapse).GetValue(myControl, null));
                    if (state)
                    {
                        return ExpandCollapseState.Expanded;
                    }
                }
                return ExpandCollapseState.Collapsed;
            }
        }

        #endregion

        #region IToggleProvider Members

        public void Toggle()
        {
            if (myApiMapping.ControlIToggleProviderAvailable)
            {
                bool toogleState = ToggleState == ToggleState.On ? false : true;
                myControl.GetType().GetProperty(myApiMapping.ControlIToggleProviderToggle).SetValue(myControl, toogleState, null);
            }
        }

        public ToggleState ToggleState
        {
            get
            {
                if (myApiMapping.ControlIToggleProviderAvailable)
                {
                    bool state = Convert.ToBoolean(myControl.GetType().GetProperty(myApiMapping.ControlIToggleProviderToggleState).GetValue(myControl, null));
                    if (state)
                    {
                        return ToggleState.On;
                    }
                }
                return ToggleState.Off;
            }
        }

        #endregion

        #region IInvokeProvider Members

        public void Invoke()
        {
            if (myApiMapping.ControlIInvokeProviderAvailable)
            {
                // To Simulate Left Mouse Click on User Control.
                MouseEventArgs e = new MouseEventArgs(MouseButtons.Left, 1, 70, 20, 0);
                MethodInfo methodToInvoke = myControl.GetType().GetMethod(myApiMapping.ControlIInvokeProviderInvoke,
                    BindingFlags.NonPublic | BindingFlags.Instance);
                methodToInvoke.Invoke(myControl, new object[] { e });
            }
        }

        #endregion
    }
}
